package com.sumainfor.common.config;

import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.sql.parser.SQLStatementParser;
import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sumainfor.common.entity.DataFilterEntity;
import com.sumainfor.common.entity.SysUserVo;
import com.sumainfor.common.exception.TCException;
import com.sumainfor.common.util.ContextUtils;
import com.sumainfor.common.utlis.ParamsDataFilterUtil;
import com.sumainfor.common.utlis.ShowSqlUtils;
import com.sumainfor.common.visitor.CustomExpressionEntity;
import com.sumainfor.common.visitor.SelectVisitorImpl;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.StringReader;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author Jack·Chen
 * @Create 2019-01-02 18:14
 * 数据权限过滤拦截器
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class})})
public class SelectSqlInjector implements Interceptor {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 临时表名
     */
    private static final String TEMP_TABLE_NAME = "TANGNEST_TEMP";

    /**
     * 构建外层SQL语句模板
     */
    private static final String OUTER_SQL = String.format("select subTable.* from %s as subTable", TEMP_TABLE_NAME);

    /**
     * 默认忽略的表数据
     */
    public static final List<String> DEFAULT_IGNORE_TABLES = Lists.newArrayList(

    );

    /**
     * 当上级部门出现类型为服务中心的时候需要忽略部门的表，只过滤公司区域级别
     */
    public static final List<String> DEFAULT_IGNORE_SERVICE_CENTER_TABLES = Lists.newArrayList(
            //备件（商品）主表
            "tcepc_commodity",
            //备件（规格）主表
            "tcepc_commodity_spe",
            //备件（型号）主表
            "tcepc_commodity_spe_mo"
    );

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        //获取sql语句
        String sql = ShowSqlUtils.getSqlByInvocation(invocation);
        if (StringUtils.isBlank(sql)) {
            return invocation.proceed();
        }

        //判断是否忽略数据权限验证
        if (ShowSqlUtils.isIgnoreAuthCheck(invocation)) {
            return invocation.proceed();
        }

        //获取自定义数据权限参数内容
        DataFilterEntity filterEntity = null;
        Object param = invocation.getArgs()[1];
        if (param instanceof Map) {
            Map map = (Map) param;
            if (map.values().size() != 0) {
                for (Object obj : map.values()) {
                    if (obj instanceof DataFilterEntity) {
                        filterEntity = (DataFilterEntity) obj;
                        break;
                    } else if (obj instanceof Map) {
                        if (((Map) obj).containsKey(ParamsDataFilterUtil.DATA_FILTER_KEY)) {
                            Object filter = ((Map) obj).get(ParamsDataFilterUtil.DATA_FILTER_KEY);
                            if (filter instanceof DataFilterEntity == false) {
                                throw new TCException("此Key值 {" + ParamsDataFilterUtil.DATA_FILTER_KEY + "} 属于数据过滤专用，不可使用");
                            }
                            filterEntity = (DataFilterEntity) filter;
                            break;
                        }
                    }
                }
            }
        }
        logger.info("DataFilterEntity 权限自定义类 => {}\n", filterEntity);


        //构建权限过滤结构
//        CustomExpressionEntity expressionEntity = getExpression(filterEntity);
        CustomExpressionEntity expressionEntity = test();  //TODO 测试用

        CCJSqlParserManager parserManager = new CCJSqlParserManager();
        Select select = (Select) parserManager.parse(new StringReader(OUTER_SQL));
        select.getSelectBody().accept(new SelectVisitorImpl(expressionEntity));
        String resultSql = select.toString();

        String innerSql = getInnerSql(sql);

        logger.info("解析的内层SQL: \n{}\n", innerSql);

        resultSql = resultSql.replaceAll(TEMP_TABLE_NAME, String.format("( %s )", innerSql));

        logger.info("Interceptor 处理后执行SQL === \n{}\n", resultSql);

        // 包装sql后，重置到invocation中
        ShowSqlUtils.resetSql2Invocation(invocation, resultSql);
        return invocation.proceed();

    }


    /**
     * 测试使用
     *
     * @return
     */
    public CustomExpressionEntity test() {
        CustomExpressionEntity expressionEntity = new CustomExpressionEntity();
        List<CustomExpressionEntity.ItemEntity> expressionList = Lists.newArrayList();
        CustomExpressionEntity.ItemEntity<LongValue> area = new CustomExpressionEntity.ItemEntity();
        area.setField("id");
        area.setAction("<>");
        area.setVal(new LongValue(-1));
        expressionList.add(area);
        expressionEntity.setIgnoreTableList(Lists.newArrayList());
        expressionEntity.setWhere(expressionList);
        return expressionEntity;
    }

    @SuppressWarnings("all")
    private CustomExpressionEntity getExpression(DataFilterEntity entity) {
        CustomExpressionEntity expressionEntity = new CustomExpressionEntity();
        //需要忽略的表数据
        List<String> ignoreTable = Lists.newArrayList();
        ignoreTable.addAll(DEFAULT_IGNORE_TABLES);

        SysUserVo user = ContextUtils.getSysUser();
        if (user == null) {
            throw new TCException("没有获取到当前登录的用户 ERROR");
        }

        //判断是否有服务中心上级关系
        Set<Integer> centerService = user.getDefaultServiceDeptChild();
        Set<Integer> bottomDeptList = user.getBottomDeptList();

        if (!CollectionUtils.isEmpty(centerService)) {
            ignoreTable.addAll(DEFAULT_IGNORE_SERVICE_CENTER_TABLES);
        }

        if (entity == null) {
            //使用默认的
            expressionEntity.setIgnoreTableList(ignoreTable);
            List<CustomExpressionEntity.ItemEntity> expressionList = Lists.newArrayList();
            CustomExpressionEntity.ItemEntity<LongValue> area = new CustomExpressionEntity.ItemEntity();
            area.setField("areaId");
            area.setAction("=");
            area.setVal(new LongValue(user.getAreaId()));
            expressionList.add(area);

            CustomExpressionEntity.ItemEntity<List<LongValue>> dept = new CustomExpressionEntity.ItemEntity();
            dept.setField("deptId");
            dept.setAction("in");
            //允许的部门编号ID
            Set<LongValue> allowDeptId = Sets.newHashSet();
            //循环插入数据
            if (!CollectionUtils.isEmpty(centerService)) {
                allowDeptId.addAll(centerService.stream().map(v1 -> new LongValue(v1)).collect(Collectors.toSet()));
                if (!CollectionUtils.isEmpty(bottomDeptList)) {
                    allowDeptId.addAll(bottomDeptList.stream().map(v2 -> new LongValue(v2)).collect(Collectors.toSet()));
                }
            } else {
                if (!CollectionUtils.isEmpty(bottomDeptList)) {
                    allowDeptId.addAll(bottomDeptList.stream().map(v3 -> new LongValue(v3)).collect(Collectors.toSet()));
                }
            }
            if (CollectionUtils.isNotEmpty(allowDeptId)) {
                dept.setVal(Lists.newArrayList(allowDeptId));
                expressionList.add(dept);
            }
            expressionEntity.setWhere(expressionList);
        } else {
            //自定义的
            //最高权限
            if (entity.isIgnore()) {
                return expressionEntity;
            }

            //自定义需要忽略的表
            if (entity.getIgnoreTables() != null) {
                ignoreTable.addAll(entity.getIgnoreTables());
            }
            expressionEntity.setIgnoreTableList(ignoreTable);

            //true: 忽略所有的权限 只能查看当前用户所在公司区域的全部数据
            List<CustomExpressionEntity.ItemEntity> expressionList = Lists.newArrayList();
            CustomExpressionEntity.ItemEntity<LongValue> area = new CustomExpressionEntity.ItemEntity();
            area.setField("areaId");
            area.setAction("=");
            area.setVal(new LongValue(user.getAreaId()));
            expressionList.add(area);
            if (entity.isIgnoreExcludeArea()) {
                expressionEntity.setWhere(expressionList);
                return expressionEntity;
            }


            //subDept  true：拥有子部门数据权限 false: 只能看到自己的数据权限
            if (entity.isSubDept() == false) {
                CustomExpressionEntity.ItemEntity<LongValue> dept = new CustomExpressionEntity.ItemEntity();
                dept.setAction("=");
                dept.setField("deptId");
                dept.setVal(new LongValue(user.getDeptId()));
                expressionEntity.setWhere(expressionList);
                return expressionEntity;
            }


            //允许的部门编号ID
            Set<LongValue> allowDeptId = Sets.newHashSet();
            CustomExpressionEntity.ItemEntity<List<LongValue>> dept = new CustomExpressionEntity.ItemEntity();
            dept.setField("deptId");
            dept.setAction("in");
            //循环插入数据
            if (!CollectionUtils.isEmpty(centerService)) {
                allowDeptId.addAll(centerService.stream().map(v1 -> new LongValue(v1)).collect(Collectors.toSet()));
                if (!CollectionUtils.isEmpty(bottomDeptList)) {
                    allowDeptId.addAll(bottomDeptList.stream().map(v2 -> new LongValue(v2)).collect(Collectors.toSet()));
                }
            } else {
                if (!CollectionUtils.isEmpty(bottomDeptList)) {
                    allowDeptId.addAll(bottomDeptList.stream().map(v3 -> new LongValue(v3)).collect(Collectors.toSet()));
                }
            }
            if (CollectionUtils.isNotEmpty(allowDeptId)) {
                dept.setVal(Lists.newArrayList(allowDeptId));
                expressionList.add(dept);
            }
            expressionEntity.setWhere(expressionList);
        }

        return expressionEntity;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    /**
     * 构建重构返回内部SQL语句
     *
     * @param originalSql
     * @return
     */
    public String getInnerSql(String originalSql) {
        SQLStatementParser parser = new MySqlStatementParser(originalSql);
        SQLStatement sqlStatement = parser.parseStatement();
        MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
        sqlStatement.accept(visitor);
        TableNameVisitor outputVisitor = new TableNameVisitor(null);
        sqlStatement.accept(outputVisitor);
        String table = outputVisitor.getAliasList().get(0);

        SelectPrintVisitor selectPrintVisitor = new SelectPrintVisitor(table);
        sqlStatement.accept(selectPrintVisitor);
        String sql = sqlStatement.toString();
        return sql;
    }

    /**
     * 数据库表名访问者
     */
    public class TableNameVisitor extends MySqlOutputVisitor {

        private List<String> aliasList = Lists.newArrayList();

        public TableNameVisitor(Appendable appender) {
            super(appender);
        }


        public List<String> getAliasList() {
            return aliasList;
        }

        @Override
        public boolean visit(SQLExprTableSource x) {
            SQLName table = (SQLName) x.getExpr();
            String alias = x.getAlias();
            if (StringUtils.isBlank(alias)) {
                aliasList.add(table.getSimpleName());
            } else {
                aliasList.add(alias);
            }
            return true;
        }
    }


    /**
     * 查询语句访问者
     */
    public class SelectPrintVisitor extends SQLASTVisitorAdapter {

        private String tableName;

        private boolean isSetting = false;

        public SelectPrintVisitor(String tableName) {
            this.tableName = tableName;
        }

        @Override
        public boolean visit(SQLSelectQueryBlock x) {
            if (!isSetting) {
                SQLSelectItem itemDept = new SQLSelectItem();
                SQLPropertyExpr deptId = new SQLPropertyExpr();
                deptId.setName("deptId");
                deptId.setOwner(tableName);
                itemDept.setExpr(deptId);

                SQLSelectItem itemArea = new SQLSelectItem();
                SQLPropertyExpr areaId = new SQLPropertyExpr();
                areaId.setName("areaId");
                areaId.setOwner(tableName);
                itemArea.setExpr(areaId);

                x.addSelectItem(itemDept);
                x.addSelectItem(itemArea);

                //测试用 Start
                SQLSelectItem testId = new SQLSelectItem();
                SQLPropertyExpr testExpr = new SQLPropertyExpr();
                testExpr.setName("id");
                testExpr.setOwner(tableName);
                testId.setExpr(testExpr);
                x.addSelectItem(testId);
                //测试用 END
                isSetting = true;
            }

            return true;
        }

    }
}
